#include "myhash.h"
#include "macro.h"
#include <stdio.h>

namespace daniel
{
    MyHash::MyHash(HashType type) : m_type(type)
    {
        switch (m_type)
        {
        case MD5:
            m_ctx.m_MD5_CTX=(MD5_CTX*)malloc(sizeof(MD5_CTX));
            break;
        case SHA1:
            m_ctx.m_SHA1_CTX=(SHA_CTX*)malloc(sizeof(SHA_CTX));
            break;
        case SHA224:
            m_ctx.m_SHA224_CTX=(SHA256_CTX*)malloc(sizeof(SHA256_CTX));
            break;
        case SHA256:
            m_ctx.m_SHA256_CTX=(SHA256_CTX*)malloc(sizeof(SHA256_CTX));
            break;
        case SHA384:
            m_ctx.m_SHA384_CTX=(SHA512_CTX*)malloc(sizeof(SHA512_CTX));
            break;
        case SHA512:
            m_ctx.m_SHA512_CTX=(SHA512_CTX*)malloc(sizeof(SHA512_CTX));
            break;
        }
    }
    MyHash::~MyHash()
    {
        if (m_md != nullptr)
        {
            free(m_md);
            m_md=nullptr;
        }
        switch (m_type)
        {
        case MD5:
            free(m_ctx.m_MD5_CTX);
            break;
        case SHA1:
            free(m_ctx.m_SHA1_CTX);
            break;
        case SHA224:
            free(m_ctx.m_SHA224_CTX);
            break;
        case SHA256:
            free(m_ctx.m_SHA256_CTX);
            break;
        case SHA384:
            free(m_ctx.m_SHA384_CTX);
            break;
        case SHA512:
            free(m_ctx.m_SHA512_CTX);
            break;
        }
    }
    void MyHash::init()
    {
        int retVal = -1;
        switch (m_type)
        {
        case MD5:
            retVal = MD5_Init(m_ctx.m_MD5_CTX);
            break;
        case SHA1:
            retVal = SHA1_Init(m_ctx.m_SHA1_CTX);
            break;
        case SHA224:
            retVal = SHA224_Init(m_ctx.m_SHA224_CTX);
            break;
        case SHA256:
            retVal = SHA256_Init(m_ctx.m_SHA256_CTX);
            break;
        case SHA384:
            retVal = SHA384_Init(m_ctx.m_SHA384_CTX);
            break;
        case SHA512:
            retVal = SHA512_Init(m_ctx.m_SHA512_CTX);
            break;
        }
        DANIEL_ASSERT2(retVal != -1, "hash init failed!\n");
    }
    void MyHash::update(void *data, size_t len)
    {
        int retVal = -1;
        switch (m_type)
        {
        case MD5:
            retVal = MD5_Update(m_ctx.m_MD5_CTX, data, len);
            break;
        case SHA1:
            retVal = SHA1_Update(m_ctx.m_SHA1_CTX, data, len);
            break;
        case SHA224:
            retVal = SHA224_Update(m_ctx.m_SHA224_CTX, data, len);
            break;
        case SHA256:
            retVal = SHA256_Update(m_ctx.m_SHA256_CTX, data, len);
            break;
        case SHA384:
            retVal = SHA384_Update(m_ctx.m_SHA384_CTX, data, len);
            break;
        case SHA512:
            retVal = SHA512_Update(m_ctx.m_SHA512_CTX, data, len);
            break;
        }
        DANIEL_ASSERT2(retVal != -1, "hash init failed!\n");
    }
    void MyHash::final()
    {
        int retVal = -1;
        switch (m_type)
        {
        case MD5:
            m_md = (unsigned char *)malloc(MD5_DIGEST_LENGTH);
            retVal = MD5_Final(m_md, m_ctx.m_MD5_CTX);
            break;
        case SHA1:
            m_md = (unsigned char *)malloc(SHA_DIGEST_LENGTH);
            retVal = SHA1_Final(m_md, m_ctx.m_SHA1_CTX);
            break;
        case SHA224:
            m_md = (unsigned char *)malloc(SHA224_DIGEST_LENGTH);
            retVal = SHA224_Final(m_md, m_ctx.m_SHA224_CTX);
            break;
        case SHA256:
            m_md = (unsigned char *)malloc(SHA256_DIGEST_LENGTH);
            retVal = SHA256_Final(m_md, m_ctx.m_SHA256_CTX);
            break;
        case SHA384:
            m_md = (unsigned char *)malloc(SHA384_DIGEST_LENGTH);
            retVal = SHA384_Final(m_md, m_ctx.m_SHA384_CTX);
            break;
        case SHA512:
            m_md = (unsigned char *)malloc(SHA512_DIGEST_LENGTH);
            retVal = SHA512_Final(m_md, m_ctx.m_SHA512_CTX);
            break;
        }
        DANIEL_ASSERT2(retVal != -1, "hash init failed!\n");
    }
    //将返回的二进制字符串转变为  16进制字节
    std::string MyHash::toHashString()
    {
        int length = 0;
        switch (m_type)
        {
        case MD5:
            length = MD5_DIGEST_LENGTH;
            break;
        case SHA1:
            length = SHA_DIGEST_LENGTH;
            break;
        case SHA224:
            length = SHA224_DIGEST_LENGTH;
            break;
        case SHA256:
            length = SHA256_DIGEST_LENGTH;
            break;
        case SHA384:
            length = SHA384_DIGEST_LENGTH;
            break;
        case SHA512:
            length = SHA512_DIGEST_LENGTH;
            break;
        }
        char *buf = (char *)malloc(length * 2);
        for (int i = 0; i < length; i++)
        {
            sprintf(buf + 2 * i, "%02x", m_md[i]);
        }
        std::string retStr(buf);
        free(buf);
        return retStr;
    }
    //一步到位
    void MyHash::hash(const unsigned char* d, size_t n)
    {
        if (m_md!=nullptr){
            free(m_md);
        }
        switch (m_type)
        {
        case MD5:
            m_md=(unsigned char*)malloc(MD5_DIGEST_LENGTH);
            ::MD5(d,n,m_md);            
            break;
        case SHA1:
            m_md=(unsigned char*)malloc(SHA_DIGEST_LENGTH);
            ::SHA1(d,n,m_md);   
            break;
        case SHA224:
            m_md=(unsigned char*)malloc(SHA224_DIGEST_LENGTH);
            ::SHA224(d,n,m_md);   
            break;
        case SHA256:
            m_md=(unsigned char*)malloc(SHA256_DIGEST_LENGTH);
            ::SHA256(d,n,m_md);   
            break;
        case SHA384:
            m_md=(unsigned char*)malloc(SHA384_DIGEST_LENGTH);
            ::SHA384(d,n,m_md);   
            break;
        case SHA512:
            m_md=(unsigned char*)malloc(SHA512_DIGEST_LENGTH);
            ::SHA512(d,n,m_md);   
            break;
        }
    }
}